package lsimElement;
/*
 * The MIT License
 *
 * Copyright 2014 David Rodenas Pico.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */



import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set;

import util.Serializer;
import lsim.LSimDispatcherHandler;
import lsim.application.ApplicationManager;
import lsim.application.handler.DummyHandler;
import lsim.application.handler.ResultHandler;
import lsim.communication.Host;
import lsim.communication.Hosts;
import lsim.worker.LSimWorker;
import lsimElement.recipesService.WorkerInitHandler;
import lsimElement.recipesService.WorkerStartHandler;
import edu.uoc.dpcs.lsim.LSimFactory;
import edu.uoc.dpcs.lsim.exceptions.LSimExceptionMessage;
import edu.uoc.sd.fr14.Configuration;
import edu.uoc.sd.fr14.exp.ExperimentFactory;
import edu.uoc.sd.fr14.exp.Experiment.ResultCollector;
import edu.uoc.sd.fr14.mesg.TargetId;
import edu.uoc.sd.fr14.model.Logger;
import edu.uoc.sd.fr14.model.ServerId;

/**
 *
 * @author drodenas
 */
public class Main implements ApplicationManager {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws InterruptedException {
        
        Configuration config = new Configuration(args);
        config.dump();
        // ExperimentConfiguration experiment = config.experiment();
        configureDebug(config);

        ResultCollector resultCollector = new ResultCollector() {
			@Override
			public void collect(boolean success, String result) {				
				System.out.println(result);
				System.exit(success ? 0:1);
			}
		};

        
        if (args.length == 0) {
        	// default use file and everything in single process
        	buildExperiments(config, resultCollector);
        } else {
        	buildExperiment(config, resultCollector);
        }
        // buildNodes(config);
        // buildNode(config);
        
        /*
        startServers();
        Thread.sleep(experiment.getCustomerDelay());
        startCustomers();
        Thread.sleep(experiment.getExecutionTime());
        stopCustomers();
        Thread.sleep(experiment.getShutdownDelay());
        stopServers();
        
        stopExperiment();
        
        Node.shutdown();
        */
    }
    
    public static void configureDebug(Configuration config) {
        configureDebug(config,"Experiment");
        
        configureDebug(config,"ExperimentComponent");
        configureDebug(config,"ToggleComponent");
        configureDebug(config,"ClockComponent");
        configureDebug(config,"ServerComponent");
        configureDebug(config,"RemoteComponent");
        configureDebug(config,"ListenerComponent");
        configureDebug(config,"CustomerComponent");

        configureDebug(config,"ModelFlight");
        configureDebug(config,"ModelNseqTracker");
        configureDebug(config,"ModelLog");
        configureDebug(config,"ModelOp");
        configureDebug(config,"ModelRole");
    }
    
    private static void configureDebug(Configuration config,String debuggable) {
        Logger.configure(debuggable, config.debug(debuggable));        
    }

    public static void buildExperiments(Configuration config, ResultCollector resultCollector) {
        
        boolean operator = true;
        for (ServerId serverId : config.servers()) {
            Configuration local = Configuration.create(config, serverId, operator);
            buildExperiment(local, resultCollector);
            operator = false;
        }
        
    }
    
    public static void buildExperiment(Configuration config, ResultCollector resultCollector) {
        
        Set<TargetId> mes = new LinkedHashSet<>();
        
        mes.addAll(config.me());
        if (config.operator()) {
            mes.add(config.customers().iterator().next());
        }
        
        ExperimentFactory.create(mes, config, resultCollector);
    }

	@Override
	public boolean isAlive() {
		return true;
	}

	@Override
	public void start() {
	}

	@Override
	public void start(LSimDispatcherHandler dispatcher) {
		try{
			process(dispatcher);
		}catch(RuntimeException e){
			LSimFactory.getWorkerInstance().logException(
					new LSimExceptionMessage("", e,	null));
		}
	}
	
	private void process(LSimDispatcherHandler dispatcher){
		LSimWorker lsim = LSimFactory.getWorkerInstance();
		lsim.setDispatcher(dispatcher);

		// set maximum time (minutes) that is expected the experiment will last
		lsim.startTimer(30);
		
		
		// ------------------------------------------------
		// init
		// ------------------------------------------------
		
		WorkerInitHandler init = new WorkerInitHandler();
		lsim.init(init);

		// getting parameters
		Properties parametersInit = init.getParams();
		Host localHost = init.getLocalHost();
		

	
		// ------------------------------------------------
		// start
		// ------------------------------------------------
		
		WorkerStartHandler start = new WorkerStartHandler();
		lsim.start(start);
		
		// get participating hosts
		Hosts participants = start.getParticipants(localHost);
		
		// 
		LSimFactory.getWorkerInstance().log(
				"", 	"-- *** --> Server -- local host: " + localHost +
				'\n' +
				"-- *** --> Server -- participants: " + participants
				);
		// 		
		// System.out.println("-- *** --> Server -- local host: "+ localHost);
		// System.out.println("-- *** --> Server -- participants: "+participants);
		
		// start 
		// TODO: inici practica
		
			
		// ------------------------------------------------
		// send final results
		// ------------------------------------------------
		
		// TODO: waits for the experiment to end and that final results are available
		// result <-- result for the experiment to send to evaluator
		
		// TODO: send result's object to the evaluator
		/*try {
			lsim.sendResult(new ResultHandler(Serializer.serialize(result)));
		} catch (IOException e) {
			e.printStackTrace();
			lsim.logException(new LSimExceptionMessage("Exception sending result", e, null));
		}*/
		
		// TODO: Waits some time before finishing
		try {
			Thread.sleep(20000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		// ------------------------------------------------
		// stop
		// ------------------------------------------------

		System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> stop");
		lsim.stop(new DummyHandler());
	}

	@Override
	public void stop() {
	}

    /*
    private static final List<Node> nodes = new LinkedList<>();
    
    public static void buildNode(Configuration config) {
        
        Node node = new Node();
        node.configure(config);
        node.buildRouter();
        nodes.add(node);
    }
    
    public static void buildNodes(Configuration config) {
        
        boolean operator = true;
        for (ServerId serverId : config.servers()) {
            Node node = new Node();
            node.configure(config);
            node.reconfigureMe(serverId, operator);
            node.buildRouter();
            nodes.add(node);
            operator = false;
        }
    }
    
    public static void startServers() {
        for (Node node : nodes) {
            node.startClock();
            node.buildServers();
        }
    }
    
    public static void stopServers() {
        for (Node node : nodes) {
            node.stopClock();
            node.stopServers();
        }
    }
    

    public static void startCustomers() {
        for (Node node : nodes) {
            node.startCustomers();
        }
    }
    
    public static void stopCustomers() {
        for (Node node : nodes) {
            node.stopCustomers();
        }
    }

    private static void stopExperiment() {
        for (Node node : nodes) {
            node.stopRouter();
        }
    }
    */
    
}
